package com.ayz.gateway.core.rpc.dubbo;


import com.ayz.gateway.core.rpc.GenericReferenceProxyFactory;
import com.ayz.gateway.core.rpc.GenericReferenceRegistry;
import com.ayz.gateway.core.rpc.config.DubboConfiguration;
import com.ayz.gateway.core.rpc.exception.DubboServiceStartFailed;
import com.ayz.gateway.core.rpc.exception.GenericReferenceProxyFactoryNotFoundException;
import com.ayz.gateway.core.rpc.utils.NamingGenerator;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.bootstrap.DubboBootstrap;
import org.apache.dubbo.rpc.service.GenericService;

import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;


/**
 * @ClassName DubboGenericReferenceRegistry
 * @Description TODO: 基于Dubbo的RPC范化代理工厂注册器
 * @Author 快乐的星球
 * @Date 2023/5/6 23:06
 * @Version 1.0
 **/
public class DubboGenericReferenceRegistry implements GenericReferenceRegistry {

    /**
     * 存储rpcResource 与 相应RPC范化代理工厂的映射
     */
    private final ConcurrentHashMap<String,DubboGenericReferenceProxyFactory> knownGenericReference=
            new ConcurrentHashMap<>(1024);
    /**
     * 网关生命周期配置项参数
     */
    private final DubboConfiguration dubboConfiguration;
    /**
     * 存储 "appliactionName@interfaceName" 与 GenericService的映射关系
     */
    private final HashMap<String,GenericService> genericServiceCache=new HashMap<>(256);

    private final ReentrantLock LOCK=new ReentrantLock();


    public DubboGenericReferenceRegistry(DubboConfiguration dubboConfiguration) {
        this.dubboConfiguration = dubboConfiguration;
    }

    /**
     * 注册一个指定的服务
     * 首先这里当前类是基于Dubbo的范化调用
     * 所以这里相当于开启了一个Dubbo的服务调用方(客户端)与指定微服务提供方建立连接
     * @param applicationName 服务的名称
     * @param interfaceName   接口的名称
     * @param rpcMethodName   rpc资源方法的名称全局唯一
     */
    @Override
    public void registryGenericReference(String applicationName, String interfaceName, String rpcMethodName) {
        String cacheKey=applicationName+"@"+interfaceName;
        GenericService genericService = this.genericServiceCache.get(cacheKey);
        if(genericService==null){
            try {
                LOCK.lock();
                genericService=this.genericServiceCache.get(cacheKey);
                if(genericService==null){
                    //注册范化调用API并开启 Dubbo服务消费客户端
                    //1. 获取Dubbo服务启动所需的配置参数
                    RegistryConfig registryConfig = dubboConfiguration.getRegistryConfig(applicationName);
                    ReferenceConfig<GenericService> referenceConfig = dubboConfiguration.getReferenceConfig(interfaceName);
                    if(referenceConfig==null || registryConfig==null){
                        throw new DubboServiceStartFailed("缺少Dubbo服务启动的必要配置参数;Dubbo启动必须同时存在ApplicationConfig,RegistryConfig,ReferenceConfig<GenericService>");
                    }
                    referenceConfig.setRegistry(registryConfig);
                    //2:启动Dubbo服务
                    DubboBootstrap bootstrap=dubboConfiguration.getDubboBootstrap();
                    bootstrap.registry(registryConfig).reference(referenceConfig).start();
                    genericService=referenceConfig.get();
                    this.genericServiceCache.put(cacheKey,genericService);
                }
            }finally {
                if(LOCK.isLocked()&&LOCK.isHeldByCurrentThread()){
                    LOCK.unlock();
                }
            }
        }
        //3:创建具体的Dubbo范化调用代理工厂
        DubboGenericReferenceProxyFactory referenceProxyFactory=new DubboGenericReferenceProxyFactory(genericService,rpcMethodName);
        //存入缓存
        String cacheKey1= NamingGenerator.generateRpcResourceName(applicationName,interfaceName,rpcMethodName);
        this.knownGenericReference.put(cacheKey1,referenceProxyFactory);
    }

    @Override
    public GenericReferenceProxyFactory getGenericReference(String rpcResourceName) {
        //如果没有找到指定的RPC资源方法则报错

        DubboGenericReferenceProxyFactory referenceProxyFactory = this.knownGenericReference.get(rpcResourceName);
        if(referenceProxyFactory==null){
            throw new GenericReferenceProxyFactoryNotFoundException("注册器中没有注册过名称为:{"+rpcResourceName+"} 的rpc资源方法");
        }
        return  referenceProxyFactory;
    }


    public ConcurrentHashMap<String, DubboGenericReferenceProxyFactory> getKnownGenericReference() {
        return knownGenericReference;
    }

    public DubboConfiguration getDubboConfiguration() {
        return dubboConfiguration;
    }

    public HashMap<String, GenericService> getGenericServiceCache() {
        return genericServiceCache;
    }


}
